#include "SerializableOutput.h"
#include "utils/logger.h"
#include "common.h"

using namespace std;
using namespace internal;

int typeSizeof(Type t) {
	switch (t) {
		case INT: return sizeof(int32_t);
		case DOUBLE: return sizeof(double);
		case BOOL: return sizeof(bool);
		default: assert(false);
	}
}

SerializableOutput::SerializableOutput(const Output& output) {
	vector<Type> columnTypes;

	for (int i = 0; i < output.size(); ++i)
		columnTypes.push_back(output[i].type);

	commonInit(columnTypes);
}

SerializableOutput::SerializableOutput(const InputOperation& input) {
	vector<Type> columnTypes;

	for (int i = 0; i < input.type_size(); ++i)
		columnTypes.push_back((Type) input.type(i));

	commonInit(columnTypes);
}

SerializableOutput::~SerializableOutput() {
	delete[] data;
	delete[] columnBegin;
}

void SerializableOutput::append(const Output& output, int rows) {
	CHECK(!full(), "SerializedOutput is already full");
	CHECK(rows <= TABLESIZE, "rows number too large");

	for (int i = 0; i < output.size(); ++i) {
		memcpy(columnBegin[i] + count * typeSizeof(output[i].type), output[i].column, rows * typeSizeof(output[i].type));
	}

	count += rows;
	*((int32_t*) data) = count;
}

bool SerializableOutput::full() const {
	return TABLESIZE + count > fullCount;
}

bool SerializableOutput::empty() const {
	return count == 0;
}

const char* SerializableOutput::serializedData() const {
	return data;
}

int SerializableOutput::serializedLength() const {
	return 2 * sizeof(int32_t) + fullCount * rowSize;
}

void SerializableOutput::clear() {
	count = 0;
	((int32_t*) data)[0] = count;
	((int32_t*) data)[1] = rowSize;
}

void SerializableOutput::deserialize(const char* str, int length) {
	CHECK(length == serializedLength(), "Invalid serialized data length");
	memcpy(data, str, length);
	count = ((int32_t*) data)[0];
	CHECK(rowSize == ((int32_t*) data)[1], "Different row sizes in serialized and deserialized data");
}

int SerializableOutput::rowsCount() const {
	return count;
}

const void* SerializableOutput::column(int i) const {
	return columnBegin[i];
}

void SerializableOutput::commonInit(const vector<Type>& columnTypes) {
	columnCount = columnTypes.size();
	columnBegin = new char*[columnCount];
	
	rowSize = 0;
	for (int i = 0; i < columnTypes.size(); ++i) {
		rowSize += typeSizeof(columnTypes[i]);
	}

	fullCount = BUFFERSIZE / rowSize + TABLESIZE;

	// to allocate data, rowSize is needed
	data = new char[serializedLength()];
	memset(data, 0, serializedLength()) ;

	char* ptr = data + 2 * sizeof(int32_t);
	for (int i = 0; i < columnTypes.size(); ++i) {
		columnBegin[i] = ptr;
		ptr += typeSizeof(columnTypes[i]) * fullCount;
	}

	CHECK(ptr == data + serializedLength(), "Wrong memory allocation");

	clear();
}
